<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>MergeConfig</title>
  </head>
  <body>
    <script>
      // forEach，遍历数组和object
      function forEach(obj, fn) {
        // 如果obj是null，或者undefined，返回
        if (obj === null || typeof obj === "undefined") {
          return;
        }

        // 如果obj不是一个object，放入数组中
        if (typeof obj !== "object") {
          obj = [obj];
        }

        // 如果obj是数组
        if (Array.isArray(obj)) {
          for (let i = 0; i < obj.length; i++) {
            fn.call(null, obj[i], i, obj);
          }
        } else {
          for (let key in obj) {
            if (Object.prototype.hasOwnProperty.call(obj, key)) {
              fn.call(null, obj[key], key, obj);
            }
          }
        }
      }

      // deepMerge，深拷贝
      /**
       * @param {Object}
       * @returns {Object}
       */
      function deepMerge() {
        // 要返回的空对象
        var result = {};

        // 定义一个递归函数
        function assignValue(val, key) {
          // 如果result[key]是object，并且 val也是object
          if (typeof result[key] === "object" && typeof val === "object") {
            result[key] = deepMerge(result[key], val);
            // 如果val是Object
          } else if (typeof val === "object") {
            result[key] = deepMerge({}, val);
          } else {
            result[key] = val;
          }
        }

        for (let i = 0; i < arguments.length; i++) {
          forEach(arguments[i], assignValue);
        }

        return result;
      }

      function isObject(obj) {
        return obj !== null && typeof obj === "object";
      }

      // 合并两个配置对象，返回一个新对象
      // config2 优先级高于 config1
      function mergeConfig(config1, config2) {
        config2 = config2 || {};
        let config = {}; // 要返回的新对象

        // 直接从config2 身上取的key值
        let valueFromConfig2Keys = ["url", "method", "params", "data"];

        // 需要深拷贝的key
        let mergeDeepPropertiesKeys = ["headers", "auth", "proxy"];

        // config2默认的key
        let defaultToConfig2Keys = [
          "baseURL",
          "url",
          "transformRequest",
          "transformResponse",
          "paramsSerializer",
          "timeout",
          "withCredentials",
          "adapter",
          "responseType",
          "xsrfCookieName",
          "xsrfHeaderName",
          "onUploadProgress",
          "onDownloadProgress",
          "maxContentLength",
          "validateStatus",
          "maxRedirects",
          "httpAgent",
          "httpsAgent",
          "cancelToken",
          "socketPath",
        ];

        // 1.遍历valueFromConfig2Keys，从config2身上赋值给config，不用考虑config1
        forEach(valueFromConfig2Keys, function (prop) {
          // 如果config2里面有key ["url", "method", "params", "data"]
          // 直接赋值给config
          if (typeof config2[prop] !== "undefined") {
            config[prop] = config2[prop];
          }
        });

        // 2.遍历mergeDeepPropertiesKeys，深克隆，优先使用config2，然后再是config1
        forEach(mergeDeepPropertiesKeys, function (prop) {
          if (isObject(config2[prop])) {
            config[prop] = deepMerge(config1[prop], config2[prop]);
          } else if (typeof config2[prop] !== "undefined") {
            config[prop] = config2[prop];
          } else if (isObject(config1[prop])) {
            config[prop] = deepMerge(config1[prop]);
          } else if (typeof config1[prop] !== "undefined") {
            config[prop] = config1[prop];
          }
        });

        // 3.遍历defaultToConfig2Keys，优先使用config2，再是config1
        // 遍历config2的默认prop
        forEach(defaultToConfig2Keys, function (prop) {
          if (typeof config2[prop] !== "undefined") {
            config[prop] = config2[prop];
          } else if (typeof config1[prop] !== "undefined") {
            config[prop] = config1[prop];
          }
        });

        // 4.合并以上3组keys => axiosKeys
        // 配置的默认标准keys，不包含用户自定义的key
        let axiosKeys = Array.from(
          new Set(
            valueFromConfig2Keys.concat(
              mergeDeepPropertiesKeys,
              defaultToConfig2Keys
            )
          )
        );

        // 5.过滤出config里面的key，axiosKeys没有的key
        let otherKeys = Array.from(
          new Set(Object.keys(config1).concat(Object.keys(config2)))
        ).filter((key) => {
          return axiosKeys.indexOf(key) === -1;
        });

        console.log(otherKeys);

        // 6.遍历otherKeys，优先从config2赋值，然后再是config1
        forEach(otherKeys, function (prop) {
          if (typeof config2[prop] !== "undefined") {
            config[prop] = config2[prop];
          } else if (typeof config1[prop] !== "undefined") {
            config[prop] = config1[prop];
          }
        });

        // 7.返回config
        return config;
      }

      let obj1 = {
        a: 12,
        b: 11,
        c: 99,
        test: 1,
        timeout: 100,
        headers: { b: 99 },
        obj: { test: 888 },
        proxy: {
          age: 11,
          port: 8000,
        },
      };
      let obj2 = {
        a: 1,
        b: 33,

        url: "www.baidu.com",
        timeout: 200,
        headers: { name: { a: 123 } },
      };
      let obj3 = mergeConfig(obj1, obj2);

      console.log(obj3);
    </script>
  </body>
</html>
